/*
 * Copyright 2012 Finbudget.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package finbudget.client.lists;

import java.io.Serializable;
import java.util.List;

import com.google.gwt.user.client.rpc.IsSerializable;
import com.gwtplatform.dispatch.annotation.GenDispatch;
import com.gwtplatform.dispatch.annotation.In;
import com.gwtplatform.dispatch.annotation.Out;
import com.gwtplatform.dispatch.shared.Action;

/**
 * This class is used to request a list from the
 * server and to get result from the server.
 * This is a substitution for GetList class.
 * GetList must be eventually deleted from the project.
 *
 */
@GenDispatch(serviceName = Action.DEFAULT_SERVICE_NAME + "GetListOfItems")
public class GetListOfItems implements IsSerializable {
	/**
	 * Available types of lists to request from the server.
	 *
	 */
	public enum Type implements Serializable, IsSerializable {
		/**
		 * A complete list of items with the specified Node.base.
		 * This kind of list is mostly used to show historical
		 * values.
		 */
		ALLITEMS,
		/**
		 * A list of items with the specified Node.base. Each
		 * item should have Node.visible flag set to true.
		 * This kind of list is mostly used in forms to enter
		 * new values.
		 */
		VISIBLEITEMS,
		/**
		 * Predefined list of users.
		 */
		LOGINS,
		/**
		 * Predefined list of users' groups.
		 */
		GROUPS,
		/**
		 * Predefined list of budget items. The result list must
		 * contain BudgetItem instances.
		 */
		BUDGETITEMS,
		/**
		 * Predefined list of scenario items. The result list
		 * must contain ScenarioItem instances.
		 */
		SCENARIOITEMS,
		/**
		 * Predefined comparison operations
		 */
		COMPARATORS,
		/**
		 * Commonly used list (only visible items). Permissions are not checked!
		 */
		COMMONLY_USED_LIST_VISIBLE_ITEMS,
		/**
		 * Commonly used list (all items). No permission checks!
		 */
		COMMONLY_USED_COMPLETE_LIST,
		/**
		 * The type that contains other types
		 */
		COMPOSITE,
		/**
		 * Returns visible items but check permission only for parent node
		 * not for every item. This is worth for long common lists (>50 items)
		 */
		VISIBLE_CHECK_ONLY_PARENT,
		/**
		 * Returns visible and hidden items but check permission on read only
		 * for one parent node not for every item. This is worth for long
		 * common lists (>50 items).
		 */
		ALL_CHECK_ONLY_PARENT,
	}
	public static class ListType implements IsSerializable, Serializable {
		private static final long serialVersionUID = -6485804222124956413L;
		/**
		 * Type of list.
		 */
		public Type type;
		/**
		 * Node.base -- string representation of Key<Node>.
		 */
		public String base;
		/**
		 * In case we need to show not a node's name in the list
		 * but one of it's properties, the property name should be
		 * passed in the substitute field.
		 */
		public String substitute;
		/**
		 * Metrics that shows time spent for getting this list from the server.
		 */
		private long timeToGetFromServer;

		public ListType() {
		}

		public ListType(Type type, String base) {
			this.type = type;
			this.base = base;
		}

		/**
		 * This method must be overriden by the ancestor class to provide proper functionality.
		 */
		public ListType clone() {
			ListType result = new ListType();
			result.type = this.type;
			result.base = this.base == null ? null : new String(this.base);
			result.substitute = this.substitute == null ? null : new String(this.substitute);
			return result;
		}

		@Override
	    public boolean equals(Object obj) {
			if (obj != null && this.getClass().equals(obj.getClass())) {
				ListType o = (ListType)obj;
				boolean equals = this.type == null && o.type == null;
				if (this.type != null) {
					equals = this.type.equals(o.type);
				}
				if (equals) {
					equals = this.base == null && o.base == null;
					if (this.base != null) {
						equals = this.base.equals(o.base);
					}
					if (equals) {
						equals = this.substitute == null && o.substitute == null;
						if (this.substitute != null) {
							equals = this.substitute.equals(o.substitute);
						}
					}
				}
				return equals;
			}
			return false;
	    }

		@Override
		public int hashCode() {
			return base == null ? 0 : base.hashCode();
		}

		public ListType setSubstitute(String substitute) {
			this.substitute = substitute;
			return this;
		}

		public long getTimeToGetFromServer() {
			return timeToGetFromServer;
		}

		public void setTimeToGetFromServer(long timeToGetFromServer) {
			this.timeToGetFromServer = timeToGetFromServer;
		}
	}
	public static class PeriodListType extends ListType {
		private static final long serialVersionUID = 6663535291009583209L;
		// Predefined calendar type analytics. DO NOT MODIFY!
		public static final String fieldName = "caltype";
		public String periodType;

		@Override
		public PeriodListType clone() {
			PeriodListType result = new PeriodListType();
			ListType type = super.clone();
			result.base = type.base;
			result.type = type.type;
			result.substitute = type.substitute;
			result.periodType = this.periodType == null ? null : new String(this.periodType);
			return result;
		}
	}
	public static class BudgetListItem implements IsSerializable {
		public String name;
		public String key;
		public BudgetListItem() {
		}
		public BudgetListItem(String key, String name) {
			this.key = key;
			this.name = name;
		}
	}
	public static class BudgetItem extends BudgetListItem {
		/**
		 * The field name inside XML structure stored in Node.description
		 * that stores information about Budget Item type. The stored type
		 * is a string representation of Key<Node>. Predefined values stored
		 * in CompanyContext object: debit -- CompanyContext.getBudgetItemDebitType
		 * credit -- CompanyContext.getBudgetItemCreditType.
		 */
		public static final String fieldName = "accounttype";
		public boolean isForDebit;
		public boolean isForCredit;
	}
	public static class ScenarioItem extends BudgetListItem {
		// Scenario must have calendar type. We pass it to the client
		// to show just those periods which corresponds to the selected budget
		public String periodType;
	}

	/**
	 * This class is intended to store information about linked suggest fields in the forms.
	 * It is not passed to the server but used on a client to compose FilteredListType instance.
	 *
	 */
	public static class LinkedListType extends ListType {
		private static final long serialVersionUID = -5050945194106289646L;
		/**
		 * Field id of the master field
		 */
		public String masterId;
		/**
		 * Name of the property of the linked list used to
		 * filter records
		 */
		public String linkedProperty;
		/**
		 * @see FilteredListType.isExclusive
		 */
		public boolean isExclusive;

		public LinkedListType() {
		}

		public LinkedListType(Type type, String base, String masterId, String linkedProperty) {
			this.type = type;
			this.base = base;
			this.masterId = masterId;
			this.linkedProperty = linkedProperty;
			this.isExclusive = false;
		}

		@Override
		public LinkedListType clone() {
			LinkedListType result = new LinkedListType();
			ListType type = super.clone();
			result.base = type.base;
			result.type = type.type;
			result.substitute = type.substitute;
			result.masterId = this.masterId == null ? null : new String(this.masterId);
			result.linkedProperty = this.linkedProperty == null ? null : new String(this.linkedProperty);
			result.isExclusive = this.isExclusive;
			return result;

		}
	}

	public static class FilteredListType extends ListType {
		private static final long serialVersionUID = -4834828873560796718L;
		/**
		 * In case the filter is inclusive then result contains all the records
		 * which have the same value of the filtered field as the specified one.
		 * In case the filter is exclusive then result contains the records that
		 * have the value other then the specified one.
		 */
		public boolean isExclusive;
		public String filteredProperty;
		public String filteredValue;

		public FilteredListType() {
		}

		public FilteredListType(Type type, String base, boolean isExclusive, String filteredProperty, String filteredValue) {
			this.type = type;
			this.base = base;
			this.isExclusive = isExclusive;
			this.filteredProperty = filteredProperty;
			this.filteredValue = filteredValue;
		}

		@Override
		public FilteredListType clone() {
			FilteredListType result = new FilteredListType();
			ListType type = super.clone();
			result.base = type.base;
			result.type = type.type;
			result.substitute = type.substitute;
			result.isExclusive = this.isExclusive;
			result.filteredProperty = this.filteredProperty == null ? null : new String(this.filteredProperty);
			result.filteredValue = this.filteredValue == null ? null : new String(this.filteredValue);
			return result;
		}

		@Override
	    public boolean equals(Object obj) {
			boolean equals = super.equals(obj);
			if (equals) {
				equals = obj != null && obj instanceof FilteredListType;
				if (equals) {
					FilteredListType object = (FilteredListType) obj;
					equals = this.isExclusive == object.isExclusive;
					if (equals) {
						equals = this.filteredProperty == null && object.filteredProperty == null;
						if (!equals && this.filteredProperty != null) {
							equals = this.filteredProperty.equals(object.filteredProperty);
						}
						if (equals) {
							equals = this.filteredValue == null && object.filteredValue == null;
							if (!equals && this.filteredValue != null) {
								equals = this.filteredValue.equals(object.filteredValue);
							}
						}
					}
				}
			}
			return equals;
		}
	}

	public static class ExtendedListType extends ListType {
		private static final long serialVersionUID = -8808940186460190668L;
		public String extendedPropertyName;

		public ExtendedListType() {
		}

		public ExtendedListType(Type type, String base, String extendedPropertyName) {
			this.type = type;
			this.base = base;
			this.extendedPropertyName = extendedPropertyName;
		}

		public ExtendedListType clone() {
			ExtendedListType result = new ExtendedListType();
			ListType type = super.clone();
			result.base = type.base;
			result.type = type.type;
			result.substitute = type.substitute;
			result.extendedPropertyName = this.extendedPropertyName == null ? null : new String(this.extendedPropertyName);
			return result;
		}

		@Override
	    public boolean equals(Object obj) {
			boolean equals = super.equals(obj);
			if (equals) {
				equals = obj != null && obj instanceof ExtendedListType;
				if (equals) {
					ExtendedListType object = (ExtendedListType) obj;
					equals = this.extendedPropertyName == null && object.extendedPropertyName == null;
					if (this.extendedPropertyName != null && !equals) {
						equals = this.extendedPropertyName.equals(object.extendedPropertyName);
					}
				}
			}
			return equals;
		}
	}

	public static class ExtendedBudgetListItem extends BudgetListItem {
		public String extendedValue;
	}

	@In(1)
	ListType listType;
	@In(2)
	int offset;

	@Out(1)
	List<BudgetListItem> list;
	@Out(2)
	String listName;
	/**
	 * Some list types initially doesn't have base on the client. So as to provide theirs update
	 * when table of corresponding items is updated or the new item is created fill-in base
	 * when the list is arrived on the client.
	 */
	@Out(3)
	String base;
	/**
	 * In case the list is not complete this member is set to the offset for the next fetch-request.
	 * In case the member is 0 the list is complete.
	 */
	@Out(4)
	int offsetToContinue;

}
